/* eslint-disable no-alert */
import { localStore, sessionStore } from '@/utils/store'
import cloneDeep from 'lodash.clonedeep'
import cfg from '@/config'
import { PM_MASTER_CODE_KEY, EXT_INFO, PM_LOCAL_MASTER_CODE_KEY } from '@/js/constants'
import { genRandomStr, calcPassword, isArrayEqual } from '@/utils'
import { mapState, mapActions } from 'vuex'
import dayjs from 'dayjs'

export default {
  data() {
    return {
      remoteData: cloneDeep(cfg.defaultFileContent),
      masterCode: '',
      dialog: {
        visible: false,
        data: {
          name: '',
          account: '',
          len: 16,
          type: ['number', 'char', 'punctuation']
        }
      },
      recordDetailId: ''
    }
  },
  computed: {
    ...mapState({
      localMasterCode: state => state.localMasterCode,
      records: state => state.records
    }),
    recordDetail() {
      const detail = this.records.find(r => r.id === this.recordDetailId)
      if (detail) {
        const latestHistory = detail.history[detail.history.length - 1]
        detail.hid = latestHistory.hid || latestHistory
        detail.type = latestHistory.type || ['number', 'char', 'punctuation']
        detail.len = latestHistory.len || 16
        detail.password = calcPassword({
          ...detail,
          remoteMasterCode: this.remoteMasterCode,
          localMasterCode: this.localMasterCode
        })
      }
      return detail
    },
    historyPasswords() {
      const passwords = []
      if (this.recordDetail) {
        this.recordDetail.history.filter(h => h.hid !== this.recordDetail.hid).forEach((h) => {
          passwords.unshift(calcPassword({
            account: this.recordDetail.account,
            hid: h.hid || h,
            len: h.len || 16,
            type: h.type || ['number', 'char', 'punctuation'],
            remoteMasterCode: this.remoteMasterCode,
            localMasterCode: this.localMasterCode
          }))
        })
      }
      return passwords
    }
  },
  methods: {
    ...mapActions([
      'setLocalMasterCode',
      'setOnline',
      'setRecords',
      'updateRecord',
      'deleteRecord'
    ]),
    init() {
      this.setLocalMasterCode(localStore.get(PM_LOCAL_MASTER_CODE_KEY))
      return this.$dataManager.init()
        .then(() => this.refreshData())
        .then((data) => {
          this.setOnline(true)
          return data
        })
        .then(async (data) => {
          const { remotePromise } = data
          if (!this.checkMasterCode(data.masterCode)) {
          // set to localStorage
            this.masterCode = data.masterCode
            // TODO: if remote master code === local then setItem or setItem always
            sessionStore.set(PM_MASTER_CODE_KEY, this.masterCode)
            // sync data
            if (remotePromise) {
              const remoteData = await remotePromise
              await this.refreshData(remoteData)
              this.$dataManager.save({ ...this.remoteData, masterCode: this.masterCode })
            }
          }
          sessionStore.set(PM_MASTER_CODE_KEY, this.masterCode)
        })
        .catch(() => {
        // open dialog to confirm master code
          let masterCodeInput = window.prompt('set your master code')
          masterCodeInput = (masterCodeInput || '').trim()
          if (masterCodeInput.length === 0) {
            window.alert('you must set master code')
            window.location.reload()
            return
          }
          this.masterCode = masterCodeInput
          // set to localStorage
          // TODO: if remote master code === local then setItem or setItem always
          sessionStore.set(PM_MASTER_CODE_KEY, this.masterCode)
        })
    },
    genHistory(len, type) {
      return ({ hid: genRandomStr(8), len, type })
    },
    checkMasterCode(masterCode) {
      // check if master code is setted
      const localMasterCode = sessionStore.get(PM_MASTER_CODE_KEY)
      return masterCode && (localMasterCode || masterCode !== cfg.defaultFileContent.masterCode)
    },
    async refreshData(newData, useCache = true) {
      try {
        let data = newData
        if (!data) {
          [data] = await this.$dataManager.get({ useCache })
        }
        this.remoteData = Object.assign({}, cfg.defaultFileContent, data)
        this.masterCode = data.masterCode
        const records = data.records || []
        records.forEach((record) => {
          const extInfo = record.extInfo
          EXT_INFO.forEach((ext) => {
            // eslint-disable-next-line no-param-reassign
            record[ext.key] = extInfo[ext.key] || ext.default
          })
        })
        this.setRecords({ records, append: false })
        return data
      } catch (err) {
        return new Error(err)
      }
    },
    addRecord(editData) {
      // gen record and save
      if (!this.localMasterCode) {
        const localMasterCode = window.prompt('enter your local master code')
        if (localMasterCode) {
          this.setLocalMasterCode(localMasterCode)
          localStore.set(PM_LOCAL_MASTER_CODE_KEY, localMasterCode)
        }
      }
      // TODO: validate
      if (!editData.name) {
        return
      }
      const nowStamp = dayjs().unix()
      if (!editData.isEdit) {
        // new
        const record = {
          id: genRandomStr(),
          name: editData.name,
          tagIds: [],
          createdAt: nowStamp,
          updatedAt: nowStamp,
          account: editData.account,
          history: [this.genHistory(editData.len, editData.type)],
          extInfo: {}
        }
        this.setRecords({ records: [record] })
      } else {
        // edit
        const record = this.records.find(r => r.id === this.recordDetailId)
        const newHistory = []
        if (!isArrayEqual(record.type, editData.type) || record.len !== editData.len) {
          // gen new history
          newHistory.push(this.genHistory(editData.len, editData.type))
        }
        this.updateRecord({
          ...record,
          name: editData.name,
          updatedAt: nowStamp,
          history: [
            ...record.history,
            ...newHistory
          ]
        })
      }
      this.syncData()
    },
    async syncData(title = 'syncing data') {
      this.$snotify.async('', title, () => new Promise((resolve) => {
        // 全量同步
        const records = cloneDeep(this.records)
        records.forEach((record) => {
          const extInfo = EXT_INFO.reduce((info, curr) => {
            // eslint-disable-next-line no-param-reassign
            info[curr.key] = record[curr.key]
            return info
          }, {})
          // eslint-disable-next-line no-param-reassign
          record.extInfo = extInfo
        })
        const syncData = Object.assign(this.remoteData, {
          // eslint-disable-next-line no-plusplus
          versionCode: ++this.remoteData.versionCode,
          records
        })
        return this.$dataManager.save(cloneDeep(syncData))
          .then(() => {
            this.dialog.visible = false
            resolve(
              {
                title: 'Success!!!',
                body: '',
                config: {
                  closeOnClick: true,
                  timeout: 1000
                }
              }
            )
            return this.refreshData(syncData)
          })
      }))
    },
    async uploadRecords() {
      const confirmResult = window.confirm('upload records?')
      if (confirmResult) {
        this.syncData('uploading')
      }
    },
    async downloadRecords() {
      const confirmResult = window.confirm('download records?')
      if (confirmResult) {
        this.$snotify.async('', 'downloading', () => new Promise((resolve) => {
          this.refreshData(null, { useCache: false }).then(() => resolve({
            title: 'Success!!!',
            body: '',
            config: {
              closeOnClick: true,
              timeout: 1000
            }
          }))
        }))
      }
    },
    checkLocalMasterCode() {
      if (!this.localMasterCode) {
        // TODO: optimization
        const localMasterCode = window.prompt('enter your local master code')
        if (localMasterCode) {
          this.setLocalMasterCode(localMasterCode)
          localStore.set(PM_LOCAL_MASTER_CODE_KEY, localMasterCode)
        }
        return !!localMasterCode
      }
      return true
    },
    selectItem(record) {
      const isSetted = this.checkLocalMasterCode()
      if (!isSetted) {
        return false
      }
      this.recordDetailId = record.id
      return true
    },
    regenPassword() {
      const confirmResult = window.confirm('gen new password?')
      if (confirmResult) {
        const nowStamp = dayjs().unix()
        const record = this.records.find(r => r.id === this.recordDetailId)
        this.updateRecord({
          ...record,
          updatedAt: nowStamp,
          history: [
            ...record.history,
            this.genHistory(record.len, record.type)
          ]
        })
        this.syncData()
      }
    },
    showDialog() {
      this.dialog.data.name = this.recordDetail.name
      this.dialog.data.account = this.recordDetail.account
      this.dialog.data.len = this.recordDetail.len
      this.dialog.data.type = this.recordDetail.type
      this.dialog.visible = true
    },
    resetDialog() {
      this.dialog.name = ''
      this.dialog.account = ''
      this.dialog.len = 16
      this.dialog.type = ['number', 'char', 'punctuation']
    },
    deleteItem(record) {
      const confirmResult = window.confirm(`confirm delete ${record.name} ?`)
      if (confirmResult) {
        // delete
        this.deleteRecord(record.id)
        this.syncData()
      }
    }
  }
}
